用C语言实现websocket服务器

您所在的位置:网站首页 websocket c语言编译器 用C语言实现websocket服务器

用C语言实现websocket服务器

2023-11-01 16:43| 来源: 网络整理| 查看: 265

Websocket Echo Server Demo 背景 嵌入式设备的应用开发大都依靠C语言来完成,我去研究如何用C语言实现websocket服务器也是为了在嵌入式设备中实现一个ip camera的功能,用户通过网页访问到嵌入式设备的摄像头以及音频,在学习的过程中先实现echo server是最基本的。

主要参考资源 编写 WebSocket 服务器——MDN Linux下用C编写WebSocet服务以响应HTML5的WebSocket请求 具体实现 整个websocket从握手到数据传输帧头的格式不在这里展开,具体参考编写 WebSocket 服务器——MDN,在这里只介绍一下websocket echo server的实现。

头文件及宏定义 #include #include #include #include #include #include #include /*在握手时需要进行sha1编码和base64编码, 在这里用openssl的库来实现*/ #include #include #include #include

#define BUFFER_SIZE 1024 #define RESPONSE_HEADER_LEN_MAX 1024 #define GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 数据帧头 /*-----------为了便于理解,在这里吧数据帧格式粘出来------------------- 0                   1                   2                   3  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-------+-+-------------+-------------------------------+ |F|R|R|R| opcode|M| Payload len |    Extended payload length    | |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           | |N|V|V|V|       |S|             |   (if payload len==126/127)   | | |1|2|3|       |K|             |                               | +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - + |     Extended payload length continued, if payload len == 127  | + - - - - - - - - - - - - - - - +-------------------------------+ |                               |Masking-key, if MASK set to 1  | +-------------------------------+-------------------------------+ | Masking-key (continued)       |          Payload Data         | +-------------------------------- - - - - - - - - - - - - - - - + :                     Payload Data continued ...                : + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + |                     Payload Data continued ...                | +---------------------------------------------------------------+ --------------------------------------------------------------------*/ typedef struct _frame_head {     char fin;     char opcode;     char mask;     unsigned long long payload_length;     char masking_key[4]; } frame_head; 封装套接字函数 为了使套接字使用看起来简洁一些,封装一个被动套接字函数,只需要传入监听端口和监听队列个数就可以返回套接字描述符,调用者可以直接用这个描述符accept去接收客户端连接。 int passive_server(int port,int queue) {     ///定义sockfd     int server_sockfd = socket(AF_INET,SOCK_STREAM, 0);

    ///定义sockaddr_in     struct sockaddr_in server_sockaddr;     server_sockaddr.sin_family = AF_INET;     server_sockaddr.sin_port = htons(port);     server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);

    ///bind,成功返回0,出错返回-1     if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1)     {         perror("bind");         exit(1);     }     ///listen,成功返回0,出错返回-1     if(listen(server_sockfd,queue) == -1)     {         perror("listen");         exit(1);     }     printf("监听%d端口\n",port);     return server_sockfd; } Base64编码函数 握手函数会用到 int base64_encode(char *in_str, int in_len, char *out_str) {     BIO *b64, *bio;     BUF_MEM *bptr = NULL;     size_t size = 0;

    if (in_str == NULL || out_str == NULL)         return -1;

    b64 = BIO_new(BIO_f_base64());     bio = BIO_new(BIO_s_mem());     bio = BIO_push(b64, bio);

    BIO_write(bio, in_str, in_len);     BIO_flush(bio);

    BIO_get_mem_ptr(bio, &bptr);     memcpy(out_str, bptr->data, bptr->length);     out_str[bptr->length-1] = '\0';     size = bptr->length;

    BIO_free_all(bio);     return size; } 逐行读取函数 握手函数循环调用,每次获得一行字符串,返回下一行开始位置 /**  * @brief _readline  * read a line string from all buffer  * @param allbuf  * @param level  * @param linebuf  * @return  */ int _readline(char* allbuf,int level,char* linebuf) {     int len = strlen(allbuf);     for (;level     //next line's point num     int level = 0;     //all request data     char buffer[BUFFER_SIZE];     //a line data     char linebuf[256];     //Sec-WebSocket-Accept     char sec_accept[32];     //sha1 data     unsigned char sha1_data[SHA_DIGEST_LENGTH+1]={0};     //reponse head buffer     char head[BUFFER_SIZE] = {0};

    if (read(cli_fd,buffer,sizeof(buffer))             strcat(linebuf,GUID); //            printf("key:%s\nlen=%d\n",linebuf+19,strlen(linebuf+19));             SHA1((unsigned char*)&linebuf+19,strlen(linebuf+19),(unsigned char*)&sha1_data); //            printf("sha1:%s\n",sha1_data);             base64_encode(sha1_data,strlen(sha1_data),sec_accept); //            printf("base64:%s\n",sec_accept);             /* write the response */             sprintf(head, "HTTP/1.1 101 Switching Protocols\r\n" \                           "Upgrade: websocket\r\n" \                           "Connection: Upgrade\r\n" \                           "Sec-WebSocket-Accept: %s\r\n" \                           "\r\n",sec_accept);

            printf("response\n");             printf("%s",head);             if (write(cli_fd,head,strlen(head))         temp = *(str+i);         *(str+i) = *(str+len-i-1);         *(str+len-i-1) = temp;     } } 接收及存储数据帧头 调用者传一个数据帧头结构体指针用于获取解析后的帧头 解析过程依照MDN中说的结构解析就好。 int recv_frame_head(int fd,frame_head* head) {     char one_char;     /*read fin and op code*/     if (read(fd,&one_char,1)         perror("read mask");         return -1;     }     head->mask = (one_char & 0x80) == 0X80;

    /*get payload length*/     head->payload_length = one_char & 0x7F;

    if (head->payload_length == 126)     {         char extern_len[2];         if (read(fd,extern_len,2)         char extern_len[8];         if (read(fd,extern_len,8)         perror("read masking-key");         return -1;     }

    return 0; } 去掩码函数 从客户端发来的数据是经过异或加密的,我们在解析帧头的时候获取到了掩码,我们通过掩码可以解码出原数据。 /**  * @brief umask  * xor decode  * @param data 传过来时为密文,解码后的明文同样存储在这里  * @param len data的长度  * @param mask 掩码  */ void umask(char *data,int len,char *mask) {     int i;     for (i=0;i         response_head = (char*)malloc(2);         response_head[0] = 0x81;         response_head[1] = head->payload_length;         head_length = 2;     }     else if (head->payload_length         response_head = (char*)malloc(12);         response_head[0] = 0x81;         response_head[1] = 127;         memcpy(response_head+2,head->payload_length,sizeof(unsigned long long));         inverted_string(response_head+2,sizeof(unsigned long long));         head_length = 12;     }

    if(write(fd,response_head,head_length)     int ser_fd = passive_server(4444,20);

    struct sockaddr_in client_addr;     socklen_t addr_length = sizeof(client_addr);     int conn = accept(ser_fd,(struct sockaddr*)&client_addr, &addr_length);

    shakehands(conn);

    while (1)     {         frame_head head;         int rul = recv_frame_head(conn,&head);         if (rul < 0)             break; //        printf("fin=%d\nopcode=0x%X\nmask=%d\npayload_len=%llu\n",head.fin,head.opcode,head.mask,head.payload_length);

        //echo head         send_frame_head(conn,&head);         //read payload data         char payload_data[1024] = {0};         int size = 0;         do {             int rul;             rul = read(conn,payload_data,1024);             if (rul             svc_websocket = new WebSocket(wsServer);         } catch (evt) {             console.log("new WebSocket error:" + evt.data);             svc_websocket = null;             if (typeof(connCb) != "undefined" && connCb != null)                 connCb("-1", "connect error!");             return;         }         //alert("");         svc_websocket.onopen = svc_onOpen;         svc_websocket.onclose = svc_onClose;         svc_websocket.onmessage = svc_onMessage;         svc_websocket.onerror = svc_onError;     }

    function svc_onOpen(evt) {         console.log("Connected to WebSocket server.");     }

    function svc_onClose(evt) {         console.log("Disconnected");     }

    function svc_onMessage(evt) {         console.log('Retrieved data from server: ' + evt.data);     }

    function svc_onError(evt) {         console.log('Error occured: ' + evt.data);     }

    function svc_send(msg) {         if (svc_websocket.readyState == WebSocket.OPEN) {             svc_websocket.send(msg);         } else {             console.log("send failed. websocket not open. please check.");         }     } 开源代码:https://github.com/lhc3538/my-websocket-server  



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3